package cn.ydxiaoshuai.pdf;

import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;

/**
 * Description 滴滴打车发票和行程单合并版成一个PDF
 * ProjectName worktools
 * Created by 小帅丶 on 2022-05-12 13:56.
 * Version 1.0
 */

public class PDFMergeUtil {

    // 分辨率不同像素不同。
    // 分辨率是72像素或英寸时，A4纸的图像像素是595*842、
    // 分辨率是96像素或英寸时，A4纸的图像像素是794*1123、
    // 分辨率是120像素或英寸时，A4纸的图像像素是1487*2105、
    // 分辨率是150像素或英寸时，A4纸的图像像素是1240*1754、
    // 分辨率是300像素或英寸时，A4纸的图像像素是2480*3508
    //A4 分辨率为72时 宽高像素
    private static Integer A4_72_WIDTH = 595;
    private static Integer A4_72_HEIGHT = 842;

    private static Integer TRAVEL_MAX_NUM = 8;

    private static Integer TRAVEL_NUM = 0;

    private static Integer TRAVEL_Y = 1050;
    private static Integer TRAVEL_B = 1450;
    //发票合并行程单会显示多行 可以修改此高度让其显示全行程单内容(建议最多合并8个行程单 TRAVEL_H_OFFSET=500)
    private static Integer TRAVEL_H_OFFSET= 500;
    private static Integer DEFAULT_DPI = 300;
    //这样看着相对是居中的
    private static Float ABSOLUTE_X = 12.5F;
    //这样看着上下比较均匀
    private static Integer ABSOLUTE_Y = 20;
    //这样看着上下比较均匀 基于 A4_72_WIDTH 计算
    private static Integer DEFAULT_IMG_WIDTH = 570;
    //减少创建时间。就优先创建了一个空的PDF文件
    private static File EMPTY_PDF = new File(PDFMergeUtil.class.getClassLoader().getResource("").getPath()+File.separator+"file//empty.pdf");

    private static String [] COMPANY_NAME = new String[]{"山西滴滴出行科技有限公司","北京滴滴出行科技有限公司","滴滴出行科技有限公司"};

    private static String [] TRAVEL_NAME = new String[]{"滴滴出行行程单"};

    private static String [] TRAVEL_COUNT_NAME = new String[]{"共","笔行程"};
    /**
     * @Author 小帅丶
     * @Description 滴滴打车发票和行程单合并版成一个PDF
     * @Date  2022/5/12 19:05
     * @param pdfParams - PDF数据
     * @param isBanner - 是否保存行程单顶部滴滴企业宣传图
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeDiDiInvoiceAndTravelToOnePDF(List<PDFParams> pdfParams,
                                                                         boolean isBanner) throws Exception {
        return mergeDiDiInvoiceAndTravelToOnePDF(pdfParams, isBanner, false, BufferedImage.TYPE_3BYTE_BGR);
    }
    /**
     * @Author 小帅丶
     * @Description 滴滴打车发票和行程单合并版成一个PDF
     * @Date  2022/5/12 19:05
     * @param pdfParams - PDF数据
     * @param isBanner - 是否保存行程单顶部滴滴企业宣传图
     * @param isGray - 是否进行灰度处理图片
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeDiDiInvoiceAndTravelToOnePDF(List<PDFParams> pdfParams,
                                                                          boolean isBanner,
                                                                          boolean isGray) throws Exception {
        return mergeDiDiInvoiceAndTravelToOnePDF(pdfParams, isBanner, isGray, BufferedImage.TYPE_3BYTE_BGR);
    }
    /**
     * @Author 小帅丶
     * @Description 滴滴打车发票和行程单合并版成一个PDF
     * @Date  2022/5/12 19:05
     * @param pdfParams - PDF数据
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeDiDiInvoiceAndTravelToOnePDF(List<PDFParams> pdfParams) throws Exception {
        return mergeDiDiInvoiceAndTravelToOnePDF(pdfParams, false, false, BufferedImage.TYPE_3BYTE_BGR);
    }
    /**
     * @Author 小帅丶
     * @Description 滴滴打车发票和行程单合并版成一个PDF
     * @Date  2022/5/12 19:05
     * @param pdfParams - PDF数据
     * @param isGray - 是否进行灰度处理图片
     * @param grayType - 灰度类别
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeDiDiInvoiceAndTravelToOnePDF(List<PDFParams> pdfParams,
                                                                          boolean isGray,
                                                                          Integer grayType) throws Exception {
        return mergeDiDiInvoiceAndTravelToOnePDF(pdfParams, false, isGray, grayType);
    }

    /**
     * @Author 小帅丶
     * @Description 滴滴打车发票和行程单合并版成一个PDF
     * @Date  2022/5/12 19:05
     * @param pdfParams - PDF数据
     * @param isBanner - 是否保存行程单顶部滴滴企业宣传图
     * @param isGray - 是否进行灰度处理图片
     * @param grayType - 灰度类别
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeDiDiInvoiceAndTravelToOnePDF(List<PDFParams> pdfParams,
                                                                          boolean isBanner,
                                                                          boolean isGray,
                                                                          Integer grayType) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        garyType(grayType);
        //先对PDF进行处理
        BufferedImage invoiceBI = null;
        BufferedImage travelBI = null;
        Map<PDFType, BufferedImage> allImg = new HashMap<>();
        long startTime = System.currentTimeMillis();
        for (PDFParams pdfParam : pdfParams) {
            boolean isDiDi = getPDFContent(pdfParam.getFilePath(),pdfParam.getPdfType());
            if(isDiDi){
                if (pdfParam.getPdfType().equals(PDFType.INVOICE)) {
                    invoiceBI = PDFToImageUtil.pdfToBufferedImage(pdfParam.getFilePath(), DEFAULT_DPI).get(0);
                    if (isGray) {
                        invoiceBI = grayDealImage(invoiceBI, grayType);
                    }
                    allImg.put(PDFType.INVOICE, invoiceBI);
                }
                if (pdfParam.getPdfType().equals(PDFType.TRAVEL_ITINERARY)) {
                    travelBI = PDFToImageUtil.pdfToBufferedImage(pdfParam.getFilePath(), DEFAULT_DPI).get(0);
                    if(TRAVEL_NUM>1){
                        isBanner = false;
                        System.out.println("行程单大于1,banner将隐藏");
                    }
                    if (isBanner) {
                        travelBI = travelBI.getSubimage(0, 0, travelBI.getWidth(),
                                travelBI.getHeight() - TRAVEL_B);
                    } else {
                        travelBI = travelBI.getSubimage(0, TRAVEL_Y, travelBI.getWidth(),
                                travelBI.getHeight() - (TRAVEL_Y + TRAVEL_H_OFFSET));
                    }
                    if (isGray) {
                        travelBI = grayDealImage(travelBI, grayType);
                    }
                    allImg.put(PDFType.TRAVEL_ITINERARY, travelBI);
                }
            }else{
                throw new Exception("非滴滴发票 或 行程单,请检查PDF内容"+ pdfParam.getPdfType());
            }
        }
        System.out.println("PDF转图片耗时:" + (System.currentTimeMillis() - startTime));
        //开始合并图片到PDF
        if (!allImg.isEmpty()) {
            //合成后的文件
            PdfReader reader = new PdfReader(new FileInputStream(EMPTY_PDF));
            PdfStamper stamper = new PdfStamper(reader, outputStream);
            //将所有图片放在pdf文件的第1页
            PdfContentByte over = stamper.getOverContent(1);
            for (Map.Entry<PDFType, BufferedImage> billImg : allImg.entrySet()) {
                BufferedImage bufferedImage = billImg.getValue();
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                ImageIO.write(bufferedImage, "jpg", outStream);
                //图片转com.itextpdf.text.Image
                com.itextpdf.text.Image contractSealImg = com.itextpdf.text.Image.getInstance(outStream.toByteArray());
                over.saveState();
                PdfGState pdfGState = new PdfGState();
                //给图片设置透明度
                pdfGState.setFillOpacity(1F);
                over.setGState(pdfGState);
                //缩放比例值
                double scaleX = (double) DEFAULT_IMG_WIDTH / contractSealImg.getWidth();
                //计算等比缩放的高度
                float height = (float) (scaleX * contractSealImg.getHeight());
                //设置图片位置
                //PDF从底部左下角算为原点坐标
                if (billImg.getKey().equals(PDFType.INVOICE)) {
                    contractSealImg.setAbsolutePosition(ABSOLUTE_X, A4_72_HEIGHT-height);
                }
                if (billImg.getKey().equals(PDFType.TRAVEL_ITINERARY)) {
                    contractSealImg.setAbsolutePosition(ABSOLUTE_X, 0);
                }
                //设置图片大小
                contractSealImg.scaleAbsolute(DEFAULT_IMG_WIDTH, height);
                //将图片添加到pdf文件
                over.addImage(contractSealImg);
                over.restoreState();
                outStream.close();
            }
            stamper.setFormFlattening(true);
            stamper.close();
            reader.close();
            outputStream.close();
        }
        System.out.println("合成总耗时:" + (System.currentTimeMillis() - startTime));
        return outputStream;
    }

    /**
     * @Author 小帅丶
     * @Description 普通发票合并成一个PDF
     * @Date  2022年11月1日09:58:59
     * @param pdfParams - PDF数据
     * @param isGray - 是否进行灰度处理图片
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeInvoiceOnePDF(List<PDFParams> pdfParams,
                                                           boolean isGray) throws Exception {
        return mergeInvoiceOnePDF(pdfParams,isGray,BufferedImage.TYPE_3BYTE_BGR);
    }

    /**
     * @Author 小帅丶
     * @Description 普通发票合并成一个PDF
     * @Date  2022年11月1日09:58:59
     * @param pdfParams - PDF数据
     * @param isGray - 是否进行灰度处理图片
     * @param grayType - 灰度类别
     * @return java.io.ByteArrayOutputStream
     **/
    public static ByteArrayOutputStream mergeInvoiceOnePDF(List<PDFParams> pdfParams,
                                                           boolean isGray, Integer grayType) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        if(pdfParams.size()>2){
            throw new Exception("PDF文件个数错误 最多不超过2个");
        }else{
            garyType(grayType);
            //先对PDF进行处理
            List<BufferedImage> allImg = new ArrayList<>();
            long startTime = System.currentTimeMillis();
            for (PDFParams pdfParam : pdfParams) {
                BufferedImage invoiceBI = PDFToImageUtil.pdfToBufferedImage(pdfParam.getFilePath(), DEFAULT_DPI).get(0);
                if (isGray) {
                    invoiceBI = grayDealImage(invoiceBI, grayType);
                }
                allImg.add(invoiceBI);
            }
            System.out.println("PDF转图片耗时:" + (System.currentTimeMillis() - startTime));
            //开始合并图片到PDF
            if (!allImg.isEmpty()) {
                //合成后的文件
                PdfReader reader = new PdfReader(new FileInputStream(EMPTY_PDF));
                PdfStamper stamper = new PdfStamper(reader, outputStream);
                //将所有图片放在pdf文件的第1页
                PdfContentByte over = stamper.getOverContent(1);
                for (int i = 0; i < allImg.size(); i++) {
                    BufferedImage bufferedImage = allImg.get(i);
                    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                    ImageIO.write(bufferedImage, "jpg", outStream);
                    //图片转com.itextpdf.text.Image
                    com.itextpdf.text.Image contractSealImg = com.itextpdf.text.Image.getInstance(outStream.toByteArray());
                    over.saveState();
                    PdfGState pdfGState = new PdfGState();
                    //给图片设置透明度
                    pdfGState.setFillOpacity(1F);
                    over.setGState(pdfGState);
                    //缩放比例值
                    double scaleX = (double) DEFAULT_IMG_WIDTH / contractSealImg.getWidth();
                    //计算等比缩放的高度
                    float height = (float) (scaleX * contractSealImg.getHeight());
                    //设置图片位置
                    //PDF从底部左下角算为原点坐标
                    if (i == 0) {
                        contractSealImg.setAbsolutePosition(ABSOLUTE_X, ABSOLUTE_Y);
                    } else {
                        contractSealImg.setAbsolutePosition(ABSOLUTE_X, A4_72_HEIGHT - height);
                    }
                    //设置图片大小
                    contractSealImg.scaleAbsolute(DEFAULT_IMG_WIDTH, height);
                    //将图片添加到pdf文件
                    over.addImage(contractSealImg);
                    over.restoreState();
                    outStream.close();
                }
                stamper.setFormFlattening(true);
                stamper.close();
                reader.close();
                outputStream.close();
            }
            System.out.println("合成总耗时:" + (System.currentTimeMillis() - startTime));
            return outputStream;
        }

    }
    /**
     * @Author 小帅丶
     * @Description 灰色类型验证
     * @Date  2022/11/1 10:15
     * @param grayType - 灰色类型
     * @return void
     **/
    public static void garyType(Integer grayType) throws Exception {
        switch (grayType) {
            case BufferedImage.TYPE_INT_RGB:
            case BufferedImage.TYPE_INT_ARGB:
            case BufferedImage.TYPE_INT_ARGB_PRE:
            case BufferedImage.TYPE_INT_BGR:
            case BufferedImage.TYPE_3BYTE_BGR:
            case BufferedImage.TYPE_4BYTE_ABGR:
            case BufferedImage.TYPE_4BYTE_ABGR_PRE:
            case BufferedImage.TYPE_BYTE_GRAY:
            case BufferedImage.TYPE_USHORT_GRAY:
            case BufferedImage.TYPE_BYTE_BINARY:
            case BufferedImage.TYPE_BYTE_INDEXED:
            case BufferedImage.TYPE_USHORT_555_RGB:
                break;
            default:
                throw new Exception("Unknown image type " +
                        grayType);
        }
    }

    /**
     * @Author 小帅丶
     * @Description 灰度处理图片
     * @Date  2022/5/13
     * @param bufferedImage - bufferedImage对象
     * @param grayType - 灰度类型
     * @return java.awt.image.BufferedImage
     **/
    private static BufferedImage grayDealImage(BufferedImage bufferedImage, Integer grayType) {
        BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(),
                grayType);
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                Color color = new Color(bufferedImage.getRGB(i, j));
                int gray = (int) (color.getRed() * 0.299 + color.getGreen() * 0.587 + color.getBlue() * 0.114);
                Color color_end = new Color(gray, gray, gray);
                grayImage.setRGB(i, j, color_end.getRGB());
            }
        }
        return grayImage;
    }

    /**
     * @Description 读取PDF内容判断是否是滴滴发票或行程单
     * @Author 小帅丶
     * @Date  2022/5/13
     * @param filePath - PDF路径
     * @param pdfType - PDF类型
     * @return java.lang.Boolean
     **/
    private static Boolean getPDFContent(String filePath, PDFType pdfType) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(filePath));
        boolean isDiDi = false;
        // 获得页数
        int pageNum = reader.getNumberOfPages();
        if (pageNum == 1) {
            String pdfContent = PdfTextExtractor.getTextFromPage(reader, 1);
            String[] splitContents = pdfContent.split("\n");
            if (splitContents.length > 0) {
                for (String content : splitContents) {
                    content = content.replace("—", "")
                            .replace("-", "");
                    if (pdfType.equals(PDFType.INVOICE)) {
                        if (Arrays.asList(COMPANY_NAME).contains(content)) {
                            isDiDi = true;
                            break;
                        }
                    }
                    if (pdfType.equals(PDFType.TRAVEL_ITINERARY)) {
                        if (Arrays.asList(TRAVEL_NAME).contains(content)) {
                            if (splitContents[5].contains(TRAVEL_COUNT_NAME[1])) {
                                int indexG = splitContents[5].indexOf(TRAVEL_COUNT_NAME[0]);
                                int indexB = splitContents[5].indexOf(TRAVEL_COUNT_NAME[1]);
                                Integer travelCount = Integer.parseInt(splitContents[5].substring(indexG + 1, indexB));
                                if (travelCount > TRAVEL_MAX_NUM) {
                                    throw new Exception("行程单 行程大于" + TRAVEL_MAX_NUM + "条记录，无法合并到一张A4纸张");
                                } else {
                                    TRAVEL_NUM = travelCount;
                                    isDiDi = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        } else {
            throw new Exception("滴滴发票 或 行程单 PDF 页数错误,应为:1。实际为:" + pageNum);
        }
        return isDiDi;
    }

    /**
     * @Author 小帅丶
     * @Description 创建空的PDF
     * @Date  2022/5/13 15:48
     * @param filePath - 文件保存路径
     * @param fileName - 文件保存名称
     * @return void
     **/
    public static void createEmptyPDF(String filePath,String fileName) throws Exception{
        PDDocument document = new PDDocument();
        document.addPage(new PDPage(PDRectangle.A4));
        PDDocumentInformation pdd = new PDDocumentInformation();
        pdd.setAuthor("小帅丶");
        Calendar date = new GregorianCalendar();
        date.setTime(new Date());
        pdd.setCreationDate(date);
        pdd.setCreator("https://www.ydxiaoshuai.cn");
        pdd.setKeywords("小帅丶代码 QQ/VX:783021975");
        pdd.setTitle("Empty PDF For DiDi Merge By XiaoShuai");
        pdd.setSubject("Empty PDF For DiDi Merge By XiaoShuai");
        document.setDocumentInformation(pdd);
        document.save(filePath+fileName);
        System.out.println("PDF created");
        document.close();
        document.close();
    }
}
